#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>

#define NUM 1024
#define SIZE 32
#define SEP " "

char cmd_line[NUM];//命令行的字符串信息

char *g_argv[SIZE];//切割后的字符串

//用于单独测试环境变量的数组
char g_my_val[64];


//描述重定向的选项 
#define INPUT_REDIR 1  //输入
#define OUTPUT_REDIR 2 //输出
#define APPEND_REDIR 3 //追加
#define NONE_REDIR 0

int redir_status = NONE_REDIR;


char* CheckRedir(char* start)
{
    assert(start);
    char *end = start + strlen(start)-1;//找到最后一个有效字符
    while(end >= start)
    {
        if(*end == '>')
        {
            if(*(end-1) == '>')//追加重定向
            {
                redir_status = APPEND_REDIR;
                *(end -1) = '\0';//把 > 最后一次出现的位置设为命令行字符串的结尾
                end++;//把 > 第一次出现的位置设为打开文件的起始位置
                break;
            }
            
            //输出重定向
            redir_status = OUTPUT_REDIR;
            *end = '\0';//此时的end就是 >,要把 > 替换成为 '\0' 即可
            end++;//end++是要把end放在我们要打开文件的起始位置
            break;
        }
        else if(*end  == '<')
        {
            redir_status = INPUT_REDIR;
            *end = '\0';
            end++;
            break;
        }
        else
        {
            end--;//一直找< 或 > ，直到结束
        }
    }
    
    if(end >= start)
    {
       return end; //end为要打开文件的起始位置的指针
    }
    else{
        return NULL;//没有找到 < 或  > 返回NULL
    }

}


int main()
{
    extern char** environ;//声明环境变量

    while(1)
    {
        //打印提示信息
        printf("[root@localhost myshell]# ");
        //刷新缓冲区
        fflush(stdout);
        memset(cmd_line,'\0',sizeof cmd_line);
        //获取用户键盘输入的指令选项
        if(fgets(cmd_line,sizeof cmd_line,stdin) == NULL)
        {
            continue;
        }
        //剪切掉换行符
        cmd_line[strlen(cmd_line)-1] = '\0';
        
        //分析是否有重定向的存在
        char* sep = CheckRedir(cmd_line);

        //字符串切割进行获取个个选项
        g_argv[0] = strtok(cmd_line,SEP);
        
        int index = 1;
        while(g_argv[index++] = strtok(NULL,SEP));//strtok切割子串，失败时也就是切割完成
        
        if(strcmp(g_argv[0],"ls") == 0)//特殊处理
        {
            g_argv[index++] = "--color=auto";
        }

        if(strcmp(g_argv[0],"cd") == 0)//特殊处理
        {
            if(g_argv[1] != NULL)
                chdir(g_argv[1]);

            continue;
        }

        if(strcmp(g_argv[0],"export") == 0 && g_argv[1] != NULL)//特殊处理
        {
            //拷贝测试的环境变量，单独进行拷贝
            strcpy(g_my_val,g_argv[1]);
            int ret = putenv(g_my_val);
            //if(ret == 0) printf("%s export success\n",g_argv[1]);
            continue;
        }

        //for_debug
        //for(index = 0;g_argv[index];index++)
        //    printf("g_argv[%d]:%s\n",index,g_argv[index]);
        
        //fork进程，进程替换
        pid_t id = fork();
        if(id == 0)
        {
            int fd = -1;
            if(sep != NULL)//表示有重定向
            {
                switch(redir_status)
                {
                    case INPUT_REDIR:
                        fd = open(sep,O_RDONLY);
                        dup2(fd,0);
                        break;
                    case OUTPUT_REDIR:
                        fd = open(sep,O_WRONLY | O_CREAT | O_TRUNC,0666);//只读 创建 打开清空
                        dup2(fd,1);
                        break;
                    case APPEND_REDIR:
                        fd = open(sep,O_WRONLY | O_CREAT | O_APPEND,0666);//只读 创建 追加
                        dup2(fd,1);
                        break;
                    default:
                        printf("BUG ?\n");
                        break;
                }
            }

            //printf("下面是子进程做的事情：\n");
            execvp(g_argv[0],g_argv);//进程替换
            exit(1);
        }

        //父进程回收子进程
        int status = 0;
        pid_t ret = waitpid(id,&status,0);//阻塞等待
        if(ret > 0)
        {
            printf("exit code:%d\n",WEXITSTATUS(status));
        }
    }
    return 0; 
}
